DiffUtil + DataBindingでお手軽リスト表示
はじめに
今回はMVVMでRecyclerViewにアイテムを表示させる方法です。
DiffUtilとObservableArrayListを使うことで、リストが変化した際にViewに反映されるようにしました。
実装
手順は以下の通りです。
- DiffUtil.Callbackを作る
- BindingAdapterを作る
- ViewModelにObservableArrayListを持たせる
- layoutでバインドする
DiffUtil.Callback
class ResultDiffCallback(val oldItems: List<ResultDisplayItem>, val newItems: List<ResultDisplayItem>) : DiffUtil.Callback() { override fun getOldListSize(): Int = oldItems.size override fun getNewListSize(): Int = newItems.size override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItems[oldItemPosition].itemId == newItems[newItemPosition].itemId } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItems[oldItemPosition] == newItems[newItemPosition] } }
BindingAdapter
object RecyclerViewBindingAdapter { @JvmStatic @BindingAdapter("bind:searchResult") fun showSearchResult(view: RecyclerView, items: MutableList<ResultDisplayItem>) { val adapter = view.adapter as ResultRecyclerAdapter val diff = DiffUtil.calculateDiff(ResultDiffCallback(adapter.mItems, items), true) adapter.mItems = items diff.dispatchUpdatesTo(adapter) } }
ViewModel
class ResultViewModel { val items: ObservableArrayList<ResultDisplayItem> = ObservableArrayList() fun searchExec(query: SearchQuery) { // APIなどから取得した結果をリストにセット追加する items.addAll(response.results) } }
layout
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <data> <variable name="viewModel" type="com.classmethod.app.features.result.ResultViewModel" /> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:searchResult="@{viewModel.items}" /> </FrameLayout> </layout>
補足
DiffUtilは、リストの比較をしつつ、RecyclerViewへの反映をやってくれる便利なクラスです。
Adapterのリストは自分で更新する必要がありますが、notifyをやってくれます。(下記参照)
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) { dispatchUpdatesTo(new ListUpdateCallback() { @Override public void onInserted(int position, int count) { adapter.notifyItemRangeInserted(position, count); } @Override public void onRemoved(int position, int count) { adapter.notifyItemRangeRemoved(position, count); } @Override public void onMoved(int fromPosition, int toPosition) { adapter.notifyItemMoved(fromPosition, toPosition); } @Override public void onChanged(int position, int count, Object payload) { adapter.notifyItemRangeChanged(position, count, payload); } }); }
まとめ
DiffUtilのおかげで、MVVM + RecyclerViewもやりやすくなったと感じます。
最下部スクロールで追加読み込みする画面などで威力を発揮するでしょう。
余談
Android Architecture ComponentsでもViewModelが追加されましたが、GoogleはMVVMを推奨しているんでしょうか・・・?